<?php

/*
 *   Original source code : 
 *  (c) 2008 Radim BADSI <info AT devbay DOT fr>
 * 
 *  Pour l'accès via nginx/apache, penser à ajouter www-data au group dialout : 
 * usermod -a -G dialout www-data
 * service nginx restart
 * service php5-fpm restart
 * 
 */


/* Definition des messages */

class TeleInfo extends CComponent {

    private $trame = array(
	  "ADCO" => array("taille" => 12, "unite" => "", "desc" => "numéro série compteur"),
	  "OPTARIF" => array("taille" => 4, "unite" => "", "desc" => "type d'abonnement"),
	  "ISOUSC" => array("taille" => 2, "unite" => "A", "desc" => "intensité souscrite"),
	  "BASE" => array("taille" => 9, "unite" => "Wh", "desc" => "index (base)"),
	  "HCHC" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures creuses)"),
	  "HCHP" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures pleines)"),
	  "PTEC" => array("taille" => 4, "unite" => "", "desc" => "période tarifaire en cours"),
	  "IINST" => array("taille" => 3, "unite" => "A", "desc" => "intensité instantanée"),
	  "ADPS" => array("taille" => 3, "unite" => "", "desc" => "avertissement de dépassement de la puissance souscrite"),
	  "IMAX" => array("taille" => 3, "unite" => "A", "desc" => "intensité maximale"),
	  "PAPP" => array("taille" => 5, "unite" => "W", "desc" => "puissance apparente"),
	  "HHPHC" => array("taille" => 1, "unite" => "", "desc" => "groupe horaire (Tempo/HC)"),
	  "MOTDETAT" => array("taille" => 6, "unite" => "", "desc" => "mot d'état"),
	  // EJP
	  "EJPHN" => array("taille" => 9, "unite" => "Wh", "desc" => "index (normal EJP)"),
	  "EJPHPM" => array("taille" => 9, "unite" => "Wh", "desc" => "index (pointe mobile EJP)"),
	  "PEJP" => array("taille" => 2, "unite" => "", "	desc" => "préavis période EJP"),
	  // Tempo
	  "BBRHCJB" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures creuses jours bleus Tempo)"),
	  "BBRHPJB" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures pleines jours bleus Tempo)"),
	  "BBRHCJW" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures creuses jours blancs Tempo)"),
	  "BBRHPJW" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures pleines jours blancs Tempo)"),
	  "BBRHCJR" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures creuses jours rouges Tempo)"),
	  "BBRHPJR" => array("taille" => 9, "unite" => "Wh", "desc" => "index (heures pleines jours rouges Tempo)"),
	  "DEMAIN" => array("taille" => 1, "unite" => "", "desc" => "couleur du lendemain (Tempo)"),
    );
    private $port = '';
    private $debug = false;

    public function __construct($port = "/dev/ttyAMA0") {
	  $this->port = $port;
	  $this->getTeleinfo();
    }

    /*
     * Récupération des données de trame
     * @param string prop : nom de la propriété à récupérer
     * @result array : info sur la propriété
     */

    public function __get($prop) {
	  return $this->trame[$prop];
    }

    /*
     * Récupération de toutes les données
     * @param bool valeurs : récupération des données ayant une valeur
     * @result array liste des propriétés
     */

    public function getAll($valeurs = true) {

	  if (!$valeurs) {
		return $this->trame;
	  } else {
		$trametmp = array();

		foreach ($this->trame as $info => $donnees) {

		    if (array_key_exists('valeur', $donnees)) {
			  if ($donnees['valide']) {
				$trametmp[$info] = $donnees;
			  }
		    }
		}

		return $trametmp;
	  }
    }

    /* public function getInfo($info = NULL) {
      // Pas d'info demandée => On renvoie tout le tableau
      if (is_null($info)) {
      return $this->trame;
      }

      // Si l'info existe dans le tableau => on la renvoie
      elseif (array_key_exists($info, $this->trame)) {
      return $this->trame[$info];
      }

      // Autres cas => renvoie NULL
      else {
      return NULL;
      }
      } */

    /* Recuperer une trame valide */

    private function getTeleinfo() {
	  $handle = fopen($this->port, "r");

	  $char = "";
	  $contents = "";

	  // On attend le début d'une nouvelle trame
	  while (fread($handle, 1) != chr(2));

	  // On lit tous les caractères jusqu'au début de la trame suivante
	  while ($char != chr(2)) {
		$char = fread($handle, 1);
		if ($char != chr(2))
		    $contents .= $char;
	  }


	  // Fermer le port
	  fclose($handle);

	  //file_put_contents("teleinfo.txt", $contents);
	  //echo $contents;
	  // Supprimer les caracteres debut/fin de trame
	  $trame_tmp = substr($contents, 1, -1);

	  // Separer les messages
	  $trame_tmp = str_replace(chr(13), "", $trame_tmp);  // Suppression des line feed
	  $trame_tmp = explode(chr(10), $trame_tmp);   // Découpage selon les retours chariot
	  // Verifier les checksum et supprimer les trames incorrectes
	  foreach ($trame_tmp as $key => $message) {

		$valide = true;

		// Separer l'etiquette, la valeur et le checksum
		$message = explode(chr(32), $message, 3);
		list ($etiquette, $valeur, $checksum) = $message;

		// Invalide si incomplet ou incorrect (checksum invalide)
		if (count($message) < 3 ||
			  $this->checksum($etiquette, $valeur) != $checksum ||
			  $this->messize($etiquette) != strlen($valeur)
		) {
		    $valide = false;
		}

		// Conversion des valeurs numériques
		if ($this->trame[$etiquette]["unite"] != "") {
		    $valeur = (int) $valeur;
		}

		// Suppression des "." finaux
		$valeur = trim($valeur, ".");

		$this->trame[$etiquette]["valeur"] = $valeur;
		$this->trame[$etiquette]["valide"] = $valide;
	  }

	  //return $trame_array;
    }

    /* Verifie le checksum d'un message 
     * La "checksum" est calculée sur l'ensemble des caractères allant du début du champ étiquette à la fin du champ 
     * donnée, caractère SP inclus. On fait tout d'abord la somme des codes ASCII de tous ces caractères. Pour éviter 
     * d'introduire des fonctions ASCII (00 à 1F en hexadécimal), on ne conserve que les six bits de poids faible du 
     * résultat obtenu (cette opération se traduit par un ET logique entre la somme précédemment calculée et 03Fh). 
     * Enfin, on ajoute 20 en hexadécimal. Le résultat sera donc toujours un caractère ASCII imprimable (signe, chiffre, 
     * lettre majuscule) allant de 20 à 5F en hexadécimal.
     */

    private function checksum($etiquette, $valeur) {
	  $controle = $etiquette . " " . $valeur;

	  $sum = 0;
	  for ($i = 0; $i < strlen($controle); $i++) {
		$sum += ord($controle[$i]);
	  }

	  $sum = ($sum & 0x3f) + 0x20;
	  return (chr($sum));
    }

    /* Verifie la taille des messages */

    private function messize($etiquette) {
	  /* Renvoyer la taille d'une valeur valide */
	  return $this->trame[$etiquette]["taille"];
    }

    /* Ecrire la liste des etiquettes valides */

    private function etiqlist() {
	  foreach ($this->trame as $etiquette => $definition) {
		echo($etiquette . " => " . $definition["desc"] . "\n");
	  }
    }

}
